
#include "scoreboard.h"

Err rp_rstatd_scoreboard_monitor(Scoreboard *scoreboard, int *extra)
{
	char fname[] = "rp_rstatd_scoreboard_monitor";
	char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (!scoreboard)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard");
		return E_INVALID;
	}
	
	rp_log(LOG_DEBUG, fname, "Rpointd: %d in total; %d busy, %d idle",
			scoreboard->total, 
			scoreboard->total-scoreboard->idle,
			scoreboard->idle);

	*extra = 0;
	if (scoreboard->total > config.num_child_min)
	{
		if (scoreboard->idle == config.num_child_idle)  *extra =0;

		if (scoreboard->idle < config.num_child_idle)
		{	
			/*	To create more rpointd    */
			*extra = config.num_child_idle - scoreboard->idle;		// extra > 0
		} else {
			/*	To kill *extra rpointd   */
			if ((scoreboard->total - config.num_child_min) > 
			     (scoreboard->idle - config.num_child_idle))
			{	
				*extra = config.num_child_min - scoreboard->total;	// extra < 0
			} else {
				*extra = config.num_child_idle -scoreboard->idle;	// extra < 0
			}
			
		}
	}else {	/*	To create more rpointd    */
		if (scoreboard->idle>= config.num_child_idle)
		{
			*extra = scoreboard->idle - config.num_child_idle;		// extra > 0
		} else {
			if ((config.num_child_min -scoreboard->total) > 
			     (config.num_child_idle -scoreboard->idle))
			{
				*extra = config.num_child_min-scoreboard->total;	// extra > 0
			} else {
				*extra = config.num_child_idle-scoreboard->idle;	// extra > 0
			}
		}
	}

	if (*extra > 0)
	{
		rp_log(LOG_INFO, fname, "Too few idle rpointd process, create %d more", *extra);
	} else if (*extra < 0)
	{
		rp_log(LOG_INFO, fname, "Too many idle rpointd process, shutdown %d in them", abs(*extra));
	} 
	
	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_scoreboard_init(Scoreboard *scoreboard)		// main thread to setup the scoreboard
{
	char fname[] = "rp_rstatd_scoreboard_init()";
	char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (!scoreboard)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard");
		return E_INVALID;
	}
	if (scoreboard->head)
	{
		rp_log(LOG_DEBUG, fname, "Scoreboard is already initialized");
		return E_ERROR;
	}
	
	scoreboard->head = NULL;
	scoreboard->tail = NULL;
	scoreboard->total = 0;
	scoreboard->idle = 0;

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_scoreboard_finalize(Scoreboard *scoreboard)
{
	char fname[] = "rp_rstatd_scoreboard_finalize()";
	char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);
	
	scoreboard->total = 0;
	scoreboard->idle = 0;
	scoreboard->head = NULL;
	scoreboard->tail = NULL;

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_scoreboard_register(Scoreboard *scoreboard, Worker *worker)
{
	char fname[] = "rp_rstatd_scoreboard_register()";
	char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (!scoreboard || !worker)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard/worker");
		return E_INVALID;
	}
	
	if (!scoreboard->total) 
	{
		worker->next = NULL;
		worker->prev = NULL;
		scoreboard->head = worker;
		scoreboard->tail = worker;
	} else {
		worker->next = NULL;
		worker->prev = scoreboard->tail;
		scoreboard->tail->next = worker;
		scoreboard->tail = worker;
	}
	scoreboard->total++;
	if (rp_score_get_state(worker->score) == SS_IDLE) scoreboard->idle++;

	FLOG_OUT(fname);
	return E_OK;
}

Worker *rp_rstatd_scoreboard_remove(Scoreboard *scoreboard, rp_pid_t pid)
{
	char fname[] = "rp_rstatd_scoreboard_remove()";
	char msg[] = "fatal error";
	Err err;	
	FLOG_IN(fname);

	if (!scoreboard)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard");
		return NULL;
	}
	
	/*	find the worker & remove from scoreboard  */
	Worker *worker = scoreboard->tail;
	while ( pid != worker->pid)
	{
		worker = worker->prev;
		if (!worker)
		{
			rp_log(LOG_WARNING, fname, "Cannot find the pid");
			return NULL;
		}
	}

	/* 	remove the worker	 */
	if (scoreboard->total == 1)
	{
		scoreboard->head = NULL;
		scoreboard->tail = NULL;
	} else if (worker == scoreboard->head)
	{
		scoreboard->head = scoreboard->head->next;
		scoreboard->head->prev = NULL;
	} else if (worker == scoreboard->tail)
	{
		scoreboard->tail = scoreboard->tail->prev;
		scoreboard->tail->next = NULL;
	} else {
		worker->prev->next = worker->next;
		worker->next->prev = worker->prev;
	}
	scoreboard->total--;
	if (rp_score_get_state(worker->score)== SS_IDLE) scoreboard->idle--;		

	FLOG_OUT(fname);
	return worker;	
}

Worker *rp_rstatd_scoreboard_remove_total(Scoreboard *scoreboard)	// main thread to destory the scoreboard
{
	char fname[] = "rp_rstatd_scoreboard_remove_total()";
	char msg[] = "fatal error";
	Err err;
	Worker *worker;
	FLOG_IN(fname);

	if (!scoreboard)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard");
		return NULL;
	}
	
	worker = scoreboard->head;
	scoreboard->head = NULL;
	scoreboard->tail = NULL;
	scoreboard->total = 0;
	scoreboard->idle = 0;

	FLOG_OUT(fname);
	return worker;	
}

Worker *rp_rstatd_scoreboard_remove_idle(Scoreboard *scoreboard, int extra)
{
	char fname[] = "rp_rstatd_scoreboard_remove_idle()";
	char msg[] = "fatal error";
	Err err;
	Worker *worker, *temp, *temp2;	
	FLOG_IN(fname);

	if (!scoreboard || !extra)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard");
		return NULL;
	}
	
	if (!extra) extra = scoreboard->idle;
	scoreboard->total -= extra;
	scoreboard->idle -= extra;

	temp = scoreboard->tail;
	int i;
	for (i=0; i<extra; i++)
	{	
		/*	remove the worker from the scoreboard   */
		while (rp_score_get_code(worker->score) == OP_SHUTDOWN && 
			   rp_score_get_state(worker->score) != SS_IDLE)  temp = temp->prev;		
		if (scoreboard->head == scoreboard->tail)
		{
			scoreboard->head = NULL;
			scoreboard->tail = NULL;
		} else if (temp == scoreboard->tail)
		{
			scoreboard->tail = scoreboard->tail->prev;
			scoreboard->tail->next = NULL;
		
		} else  if (temp == scoreboard->head) 
		{
			scoreboard->head = scoreboard->head->next;
			scoreboard->head->prev = NULL;				
		} else {
			temp->prev->next = temp->next;
			temp->next->prev = temp->prev;
		}
		temp2= temp;
		temp = temp->prev;
		/*	insert temp into worker list   */
		if (!worker) 
		{
			temp2->next = NULL;
			temp2->prev = NULL;
			worker = temp2;
		}  else {
			temp2->next = worker;
			temp2->prev = NULL;
			worker->prev = temp2;
			worker = temp2;
		}			
	}
	FLOG_OUT(fname);
	return worker;
}

/*
int rp_rstatd_scoreboard_total(Scoreboard *scoreboard)
{
	char fname[] = "rp_rstatd_scoreboard_total";
	char msg[] = "fatal error";
	Err err;

	err = rp_mutex_lock(&(scoreboard->mutex));
	int total = scoreboard->total;
	err = rp_mutex_unlock(&(scoreboard->mutex));
	
	return total;
}
*/

Err rp_rstatd_scoreboard_cleanup(Scoreboard *scoreboard)
{
	char fname[] = "rp_rstatd_scoreboard_cleanup()";
	char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (!scoreboard)
	{
		rp_log(LOG_DEBUG, fname, "Invalid parameters: scoreboard");
		return E_INVALID;
	}
	
	int status;
	Worker *worker = scoreboard->head;
	while (worker)
	{
		if (!waitpid(worker->pid, &status, WNOHANG))
		{	/*	the process is still there	  */
			if (worker->exitFlag) 
			{
				err = rp_process_terminate(worker->pid, SIGTERM);
				rp_log(LOG_WARNING, fname, "Forcefully terminate rpointd, PID = %d");
			} else {
				if (rp_score_get_code(worker->score) == OP_SHUTDOWN) worker->exitFlag = 1;
				worker = worker->next;
				continue;
			}
		} else {
			/*	get the process exit code   */
			if (WIFEXITED(status))
			{
				WEXITSTATUS(status);				// TODO: check rpointd exit status
				rp_log(LOG_DEBUG, fname, "Rpointd PID=%d exits with the status");
			} else if (WIFSIGNALED(status))
			{
				WTERMSIG(status);
				rp_log(LOG_DEBUG, fname, "Rpointd PID=%d exits with the signal");
			} else {
				rp_log(LOG_CRITICAL, fname, "Rpointd PID=%d exits with unknown cause");
			}
		}

		/*	remove the worker from scoreboard   */			
		if (scoreboard->total == 1)
		{
			scoreboard->head = NULL;
			scoreboard->tail = NULL;
		} else if (worker == scoreboard->head)
		{
			scoreboard->head = scoreboard->head->next;
			scoreboard->head->prev = NULL;
		} else if (worker == scoreboard->tail)
		{
			scoreboard->tail = scoreboard->tail->prev;
			scoreboard->tail->next = NULL;
		} else {
			worker->prev->next = worker->next;
			worker->next->prev = worker->prev;
		}
		scoreboard->total--;
		if (rp_score_get_state(worker) == SS_IDLE) scoreboard->idle--;
		
		/*	unmap & close the file   */
		if (worker->score)		// if score has been initialized
		{	
			err = rp_score_finalize(&(worker->score));
			if (!munmap((void*)worker->score, sizeof(Score)))
			{
				rp_log(LOG_CRITICAL, fname, "Cannot unmap the file under /tmp/rally");
			}  else {
				free(worker->score);
			}
			free(worker);	
		}
		worker = worker->next;
	}
	
	FLOG_OUT(fname);
	return err;
}


